#ifndef _PARSER_BASIC_HPP
#define _PARSER_BASIC_HPP 1
#include"lexer_basic.hpp"
#include<memory>
namespace LocalData{

}

struct basic_expr_node{
    int depth;Token type;
    std::shared_ptr<basic_expr_node>left,right;
    std::set<Token>nexts;
    basic_expr_node(int d,Token t,std::shared_ptr<basic_expr_node>lhs=nullptr,std::shared_ptr<basic_expr_node>rhs=nullptr)
        :depth(d),type(t),left(lhs),right(rhs){}
    virtual ~basic_expr_node()=default;
    virtual NumType cal(){
        switch(type){
            case number:return dynamic_cast<number_expr_node*>(this)->val;
            case identifier:
            case oper:
                return dynamic_cast<op_expr_node*>(this)->cal();
                // return 
            case func:
        }
    }
    virtual bool validNext(Token t)noexcept{return nexts.count(t)!=0;}
};

struct number_expr_node:basic_expr_node{
    NumType val;
    number_expr_node(int d,NumType v):basic_expr_node(d,number,0),val(v){
        nexts.insert(oper);
    }
    virtual ~number_expr_node()=default;
    NumType cal(){
        return val;
    }
};
struct op_expr_node:basic_expr_node{
    Oper opcode;
    op_expr_node(int d,Oper op,std::shared_ptr<basic_expr_node>lhs,std::shared_ptr<basic_expr_node>rhs):basic_expr_node(d,oper,lhs,rhs),opcode(op){
        nexts.insert({number,func,identifier});
    }
    virtual ~op_expr_node()=default;
    NumType cal(){
        if(left&&right){
            if(opcode==assign){
                LocalData::SymbolTable::Push(dynamic_cast<id_expr_node*>(left.get())->id,right->cal());
                return LocalData::SymbolTable::Get(dynamic_cast<id_expr_node*>(left.get())->id);
            }
            NumType lhs=left->cal(),rhs=right->cal();
            switch(opcode){
                case minus:return lhs-rhs;
                case plus:return lhs+rhs;
                case mult:return lhs*rhs;
                case divi:
                    if(rhs)return lhs/rhs;
                    throw LocalData::DIV_BY_0;
                case mod:
                    if(rhs)return (long long)(lhs-lhs/rhs*rhs);
                    throw LocalData::DIV_BY_0;
                case fac:
                    return std::pow(lhs,rhs);
            }
        }
        // return val;
    }
};
struct id_expr_node:basic_expr_node{
    std::string id;
    id_expr_node(int d,std::string const& ident,std::shared_ptr<basic_expr_node>lhs,std::shared_ptr<basic_expr_node>rhs)
        :basic_expr_node(d,identifier),id(ident){
            nexts.insert({oper});
        }
    ~id_expr_node()=default;
    NumType cal(){
        return LocalData::SymbolTable::Get(id);
    }
    std::string symbol()const noexcept{return id;}
};
struct fn_expr_node:basic_expr_node{
    Func fn;bool unary;
    fn_expr_node(int d,Func ident,std::shared_ptr<basic_expr_node>lhs):basic_expr_node(d,func,lhs),unary(false),fn(ident){
        nexts.insert({oper,number});
    }
    fn_expr_node(int d,Func ident,std::shared_ptr<basic_expr_node>lhs,std::shared_ptr<basic_expr_node>rhs):basic_expr_node(d,func,lhs,rhs),
        unary(true),fn(ident){nexts.insert({oper,number});}
    ~fn_expr_node()=default;
    NumType call()noexcept{
        return unary?
            LocalData::DataSingleton::entity().unaryFn[fn](left->cal()):
            LocalData::DataSingleton::entity().binaryFn[fn](left->cal(),right->cal());
    }
    NumType cal(){
        return call();
    }
};

using Lexeme=std::vector<std::pair<std::string,Token>>;
class expr_tree{
    std::string expr;
    std::shared_ptr<basic_expr_node>root;
    Lexeme lexemes;
    bool built;
        void show(std::ostream&os,std::shared_ptr<basic_expr_node>const&base)noexcept{
            if(base==nullptr)return;
            os<<"depth:"<<base->depth<<"\ttype:"<<
                LocalData::DataSingleton::entity().Tk2str[base->type]<<std::endl;
            show(os,base->left);
            show(os,base->right);
        }
    public:
        // expr_tree(const std::string&content)try:expr(content),root(new basic_expr_node(0,identifier)),built(false){}
        //     catch(std::bad_alloc const&){/* alloc error */}
        expr_tree(const Lexeme&lex)try:lexemes(lex),root(new basic_expr_node(0,identifier)),built(false){}
            catch(std::bad_alloc const&){/* alloc error */}
        ~expr_tree()=default;
        const Lexeme& SwitchLex(const Lexeme&lex){
            if(lex==lexemes)return lexemes;
            const auto&le=this->lexemes;
            built=false;
            this->lexemes=lex;
            return le;
        }
        void show(std::ostream&os)noexcept{
            show(os,root);
        }
        void build();
        NumType calculate(){
            if(built)return root->left->cal();
            throw LocalData::NODATA;
        }
};

void expr_tree::build(){
    built=true;
    
}

#endif//PARSER_BASIC_HPP
